/**
 * Copyright(c) 2010 Ceno Techonologies Co., Ltd.
 *
 * History:
 *   15-6-18 下午3:59 Created by lyyang
 */
package com.jade.bss.business.bonus;

import java.util.Date;
import java.util.List;
import java.util.Random;

import com.jade.bss.base.BssException;
import com.jade.framework.base.criterion.Criteria;
import com.jade.framework.base.util.PagingList;

/**
 * 红包管理器
 *
 * @author <a href="mailto:lyyang@ceno.cn">lyyang</a>
 * @version 1.0 15-6-18 下午3:59
 */
public abstract class BonusManager
{
    public long[] generateLuckyDrawer(Bonus bonus)
    {
        Random random = new Random();
        random.setSeed(System.currentTimeMillis());
        long[] result = new long[bonus.getQuantity()];

        long average = (bonus.getTotalAmount() - bonus.getTotalCost()) / bonus.getQuantity();
        if (bonus.getType() == Bonus.TYPE_FIX) {
            for (int i = 0; i < result.length; i++) {
                result[i] = average;
            }
            return result;
        }
        long total = bonus.getTotalAmount();
        long a = average - bonus.getMinBonus();
        long b = bonus.getMaxBonus() - bonus.getMinBonus();
        //
        //这样的随机数的概率实际改变了，产生大数的可能性要比产生小数的概率要小。
        //这样就实现了大部分红包的值在平均数附近。大红包和小红包比较少。
        long range1 = sqr(average - bonus.getMinBonus());
        long range2 = sqr(bonus.getMaxBonus() - average);

        for (int i = 0; i < result.length; i++) {
            //因为小红包的数量通常是要比大红包的数量要多的，因为这里的概率要调换过来。
            //当随机数>平均值，则产生小红包
            //当随机数<平均值，则产生大红包
            if (nextLong(random, bonus.getMinBonus(), bonus.getMaxBonus()) > average) {
                // 在平均线上减钱
                //              long temp = min + sqrt(nextLong(range1));
                long temp = bonus.getMinBonus() + xRandom(random, bonus.getMinBonus(), average);
                result[i] = temp;
                total -= temp;
            }
            else {
                // 在平均线上加钱
                //              long temp = max - sqrt(nextLong(range2));
                long temp = bonus.getMaxBonus() - xRandom(random, average, bonus.getMaxBonus());
                result[i] = temp;
                total -= temp;
            }
        }
        // 如果还有余钱，则尝试加到小红包里，如果加不进去，则尝试下一个。
        while (total > 0) {
            for (int i = 0; i < result.length; i++) {
                if (total > 0 && result[i] < bonus.getMaxBonus()) {
                    result[i]++;
                    total--;
                }
            }
        }
        // 如果钱是负数了，还得从已生成的小红包中抽取回来
        while (total < 0) {
            for (int i = 0; i < result.length; i++) {
                if (total < 0 && result[i] > bonus.getMinBonus()) {
                    result[i]--;
                    total++;
                }
            }
        }
        return result;

    }


    private long xRandom(Random random, long min, long max)
    {
        if (max == min) {
            return 0;
        }
        return sqrt(nextLong(random, sqr(max - min)));
    }


    private long sqrt(long n)
    {
        // 改进为查表？
        return (long) Math.sqrt(n);
    }

    private long sqr(long n)
    {
        // 查表快，还是直接算快？
        return n * n;
    }

    private long nextLong(Random random, long n)
    {
        return random.nextInt((int) n);
    }

    private long nextLong(Random random, long min, long max)
    {
        return random.nextInt((int) (max - min + 1)) + min;
    }


    public abstract Bonus add(Bonus bonus)
            throws BssException;

    public abstract void remove(Bonus bonus)
            throws BssException;

    public abstract void remove(long[] ids)
            throws BssException;

    public abstract void update(Bonus bonus)
            throws BssException;

    public abstract Bonus get(long id);

    public abstract PagingList<Bonus> query(Criteria criteria);

    public abstract List<Bonus> list(Criteria criteria);

    public abstract void addRecord(BonusRecord record);

    public abstract BonusRecord getRecord(long id);

    public abstract BonusRecord getRecord(String receivedType, String receivedId, String orderNo);

    public abstract void updateRecord(BonusRecord record);

    public abstract PagingList<BonusRecord> queryRecord(Criteria criteria);

    public abstract List<BonusRecord> listRecord(Criteria criteria);

    public abstract void draw(long id, long amount);

    public abstract BonusRecord getRecord(long bonusId, String ownerType, String ownerId);

    public abstract void updateRecordStatus(long id, int status);

    /**
     * 统计红包已发，已领，返回，失败
     * @param id
     * @param status
     * @param beginDate 开始时间
     * @param endDate 结束时间
     */
    public abstract long statBonusRecordCount(long id, int status, Date beginDate, Date endDate);
}
